﻿using System.Data;
using System.Collections.Generic;

using GalaSoft.MvvmLight;

using GameEngine;
using GameEngine.PDNParser;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using DaphneProfiBase;
using Microsoft.Win32;
using Tomers.WPF.Localization;
using System.IO;
using System.Windows;
using MVVMDialogs;

namespace DaphneForms.ViewModel
{
    /// <summary>
    /// ViewModel for the loaded PDN library
    /// </summary>
    public class PDNGameListVM : ViewModelBase
    {
        // data sources
        private PDNRawGameList _gameList = null;
        private DataTable _dataTable;

        private enum eSearchType { WhitePlayer, BlackPlayer, Both };

        private IDialogService _dlgService = null;

        private PDNLibraryFilter _libraryFilter;
        private bool _useLibraryFilter = false;

        /// <summary>
        /// Constructor
        /// </summary>
        public PDNGameListVM()
        {
            _dlgService = ServiceLocator.Resolve<IDialogService>();


            _dataTable = new DataTable();
            BothPlayers = true;
            _useLibraryFilter = false;
            _libraryFilter = null;

            // register commands
            SearchCommand = new RelayCommand(() => SearchGames());
            LoadDifferentPDNCommand = new RelayCommand(() => LoadDifferentPDN());
            LoadGameCommand = new RelayCommand(() => LoadGame());
            CancelCommand = new RelayCommand(() => CancelForm());
            SetupAdvancedFilterCommand = new RelayCommand(() => SetupAdvancedFilter());
            ClearAdvancedFilterCommand = new RelayCommand(() =>
                {
                    _libraryFilter = null;
                    SearchGames();
                });
        }

        /// <summary>
        /// Sets the list of loaded games
        /// </summary>
        public void SetPDNGameList(PDNRawGameList gameList)
        {
            _gameList = gameList;

            PrepareDataTable(eSearchType.Both, string.Empty);
        }

        public PDNRawGame GameToLoad
        { get; set; }

        public PDNRawGameList LoadedGameList
        {
            get
            {
                return _gameList;
            }
        }

        #region Binding Properties

        public bool UseLibraryFilter
        {
            get
            {
                return _useLibraryFilter;
            }
            set
            {
                _useLibraryFilter = value;
                SearchGames();
            }
        }
    

        public DataTable GameList
        {
            get
            {
                return _dataTable;
            }
        }

        public string SearchedText
        { get; set; }

        public RelayCommand SearchCommand
        { get; set; }

        public RelayCommand LoadDifferentPDNCommand
        { get; set; }

        public RelayCommand LoadGameCommand
        { get; set; }

        public RelayCommand CancelCommand
        { get; set; }

        public RelayCommand SetupAdvancedFilterCommand
        { get; set; }

        public RelayCommand ClearAdvancedFilterCommand
        { get; set; }

        public bool WhitePlayer
        { get; set; }

        public bool BlackPlayer
        { get; set; }

        public bool BothPlayers
        { get; set; }

        public int SelectedDataRow
        { get; set; }   

        #endregion


        /// <summary>
        /// Method for running the code of advanced library filter
        /// </summary>
        private void SetupAdvancedFilter()
        {
            // run the form
            PDNFilterFormVM vm = new PDNFilterFormVM();
            if (_libraryFilter != null)
                vm.LibraryFilter = _libraryFilter;

            _dlgService.ShowDialog<PDNFilterForm>(this, vm);

            // if success, set the new library filter
            if (vm.Success)
            {
                _libraryFilter = vm.LibraryFilter;
                UseLibraryFilter = true;

                RaisePropertyChanged("UseLibraryFilter");

                // SearchGames();
            }
        }


        /// <summary>
        /// Loads different PDN file
        /// </summary>
        private void LoadDifferentPDN()
        {
            // show open file dialog
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.DefaultExt = ".pdn";
            dlg.Filter = LanguageContext.Instance.Dictionary.Translate<string>("PDNFilter", "Value");
            dlg.CheckFileExists = true;
            dlg.CheckPathExists = true;
            if (dlg.ShowDialog() == true)
            {
                // if successful, load the dialog
                FileStream fileStream = new FileStream(dlg.FileName, FileMode.Open);

                // read the file
                PDNRawGameList gameList;
                PDNReader reader = new PDNReader();
                if (reader.ParseFile(fileStream, out gameList) == false)
                {
                    string sCaption = LanguageContext.Instance.Dictionary.Translate<string>("Error", "Value");
                    MessageBox.Show(reader.ErrorMessage, sCaption, MessageBoxButton.OK, MessageBoxImage.Error);
                    return;
                }
                fileStream.Close();

                // setup the form
                _gameList = gameList;
                
                SearchedText = string.Empty;
                RaisePropertyChanged("SearchedText");

                PrepareDataTable(eSearchType.Both, string.Empty);

                // refresh columns in the datagrid
                Messenger.Default.Send<NotificationMessage>(new NotificationMessage(this, Notify.ResetDataGrid));
            }
        }
        
        /// <summary>
        /// Loads the game
        /// </summary>
        private void LoadGame()
        {
            // if no row selected, exit
            if (SelectedDataRow < 0)
                return;

            // get the game index
            int gameIndex = _dataTable.Rows[SelectedDataRow].Field<int>(0);

            // set the game to load
            GameToLoad = _gameList.Games[gameIndex - 1];                      

            // close the form
            Messenger.Default.Send<NotificationMessage>(new NotificationMessage(this, Notify.CloseForm));
        }
        
        /// <summary>
        /// Cancels the form
        /// </summary>
        private void CancelForm()
        {
            // set no game to load
            GameToLoad = null;

            // close the form
            Messenger.Default.Send<NotificationMessage>(new NotificationMessage(this, Notify.CloseForm));
        }

        /// <summary>
        /// Command handler for basic filtering option - searching by the names of players
        /// </summary>
        private void SearchGames()
        {
            // determine search type
            eSearchType searchType = eSearchType.Both;
            if (WhitePlayer)
                searchType = eSearchType.WhitePlayer;
            else if (BlackPlayer)
                searchType = eSearchType.BlackPlayer;

            string searchValue = SearchedText == null ? string.Empty : SearchedText.Trim();

            // do the search
            PrepareDataTable(searchType, searchValue);
        }

        /// <summary>
        /// Prepares the list of games
        /// </summary>
        private void PrepareDataTable(eSearchType searchType, string searchedValue)
        {
            // clear the old values
            _dataTable.Clear();
            _dataTable.Columns.Clear();

            // prepare the list of columns
            List<string> pdnKeys = new List<string>();
            foreach(var game in _gameList.Games)
            {
                foreach (var pdnHeader in game.Headers)
                {
                    if (pdnKeys.Contains(pdnHeader.Key) == false)
                        pdnKeys.Add(pdnHeader.Key);
                }
            }

            // add columns to the datatable
            _dataTable.Columns.Add("-", typeof(int));
            foreach (var pdnKey in pdnKeys)
            {
                _dataTable.Columns.Add(pdnKey, typeof(string));
            }

            // TO-DO: searching according to the basic filter and advanced filter
            
            // add game values
            int gameIndex = 0;
            foreach (var game in _gameList.Games)
            {
                gameIndex++;

                // check the library filter
                if (AppliesForLibraryFilter(game) == false)
                    continue;

                // do filtering by the names of players
                if (string.IsNullOrWhiteSpace(searchedValue) == false)
                {
                    if (AppliesForBasicFilter(game, searchType, searchedValue) == false)
                        continue;
                }

                DataRow row = _dataTable.NewRow();

                row.SetField<int>(0, gameIndex);                
                // add all row values
                foreach (var pdnHeader in game.Headers)
                {
                    row.SetField<string>(pdnHeader.Key, pdnHeader.Value);
                }

                _dataTable.Rows.Add(row);
            }   
 
            // notify the property has changed
            RaisePropertyChanged("GameList");
        }

        /// <summary>
        /// Checkers whether given game is compatible with the basic filter
        /// </summary>
        private bool AppliesForBasicFilter(PDNRawGame game, eSearchType searchType, string searchedValue)
        {
            // get the values
            string whitePlayer = GetPDNKey(game, PDNHeaderKey.White);
            string blackPlayer = GetPDNKey(game, PDNHeaderKey.Black);

            // check white player name
            if (searchType == eSearchType.Both || searchType == eSearchType.WhitePlayer)
            {
                if (whitePlayer.Contains(searchedValue))
                    return true;
            }

            // check black player name
            if (searchType == eSearchType.Both || searchType == eSearchType.BlackPlayer)
            {
                if (blackPlayer.Contains(searchedValue))
                    return true;
            }

            return false;
        }

        /// <summary>
        /// Checkes whether the given game applies for the library filter set
        /// </summary>
        private bool AppliesForLibraryFilter(PDNRawGame game)
        {
            // if no filter set, the game automatically applies
            if(_libraryFilter == null || _useLibraryFilter == false)
                return true;

            // iterate through all set PDN keys
            foreach (var pair in _libraryFilter)
            {
                // get the key
                string keyValue = GetPDNKey(game, pair.Key);
                
                // if key not present, exit
                if (string.IsNullOrWhiteSpace(keyValue))
                    return false;

                // check the key value - must be substring
                if (keyValue.Contains(pair.Value) == false)
                    return false;
            }

            // success, the game passed all the criteria set
            return true;
        }

        /// <summary>
        /// Returns the PDN key
        /// </summary>
        private string GetPDNKey(PDNRawGame game, string pdnKey)
        {
            foreach (var header in game.Headers)
            {
                if (header.Key == pdnKey)
                    return header.Value;
            }

            return string.Empty;        
        }
    }
}
